#include "yaml.h"
#include <iostream>
#include "db_conninfo.h"
#include "opengauss/libpq-fe.h"
#include <unordered_map>
#include <iostream>

std::string getsource(const char *location)
{
    YAML::Node config = YAML::LoadFile(location);
    std::string datasource;
    datasource.append("dbname=");
    datasource.append(config["diff"]["source"]["database"].as<std::string>());
    datasource.append(" port=");
    datasource.append(config["diff"]["source"]["port"].as<std::string>());
    datasource.append(" host=");
    datasource.append(config["diff"]["source"]["host"].as<std::string>());
    datasource.append(" application_name=libpq connect_timeout=5 sslmode=allow");
    datasource.append(" user=");
    datasource.append(config["diff"]["source"]["username"].as<std::string>());
    if (!config["diff"]["source"]["password"].IsNull())
    {
        datasource.append(" password=");
        datasource.append("'");
        datasource.append(config["diff"]["source"]["password"].as<std::string>());
        datasource.append("'");
    }
    return datasource;
}

std::string getsourceurl(const char *location)
{
    YAML::Node config = YAML::LoadFile(location);
    // 'postgresql://test:test_1234@127.0.0.1:7654/postgres?connect_timeout=5&application_name=test&sslmode=allow'
    //  postgresql://opengauss:null@127.0.0.1:7654/postgres?connect_timeout=5&application_name=test&sslmode=allow

    std::string source;
    source.append("postgresql://");
    source.append(config["diff"]["source"]["username"].as<std::string>());
    source.append(":");
    source.append(config["diff"]["source"]["password"].as<std::string>());
    source.append("@");
    source.append(config["diff"]["source"]["host"].as<std::string>());
    source.append(":");
    source.append(config["diff"]["source"]["port"].as<std::string>());
    source.append("/");
    source.append(config["diff"]["source"]["database"].as<std::string>());
    source.append("?connect_timeout=5&application_name=test&sslmode=allow");
    // std::cout << source << std::endl;
    return source;
}

std::string getdestination(const char *location)
{
    YAML::Node config = YAML::LoadFile(location);
    std::string destination;
    destination.append("dbname=");
    destination.append(config["diff"]["destination"]["database"].as<std::string>());
    destination.append(" port=");
    destination.append(config["diff"]["destination"]["port"].as<std::string>());
    destination.append(" host=");
    destination.append("'");
    destination.append(config["diff"]["destination"]["host"].as<std::string>());
    destination.append("'");
    destination.append(" application_name=libpq connect_timeout=5 sslmode=allow");
    destination.append(" user=");
    destination.append(config["diff"]["destination"]["username"].as<std::string>());
    if (!config["diff"]["destination"]["password"].IsNull())
    {
        destination.append(" password=");
        destination.append("'");
        destination.append(config["diff"]["destination"]["password"].as<std::string>());
        destination.append("'");
    }
    return destination;
}
static void
exit_nicely(PGconn *conn)
{
    PQfinish(conn);
    exit(1);
}

int QuerySettings(PGconn *conn, const char *sql, std::unordered_map<std::string, std::string> &configMap)
{
    int nFields;
    int i, j;

    // 执行查询语句
    PGresult *res = PQexec(conn, sql);
    if (PQresultStatus(res) != PGRES_TUPLES_OK)
    {
        fprintf(stderr, "查询失败: %s", PQerrorMessage(conn));
        PQclear(res);
        PQfinish(conn);
        return 1;
    }

    // 获取结果集中的行数
    int rows = PQntuples(res);
    nFields = PQnfields(res);
    /* 打印行 */
    char result[1024];
    for (j = 0; j < nFields; j++)
    {
        // 获取你需要的两个字段的列索引，假设需要 column2 和 column4
        int column_index1 = PQfnumber(res, "\"name\"");
        int column_index2 = PQfnumber(res, "\"setting\"");
        if (column_index1 == -1 || column_index2 == -1)
        {
            fprintf(stderr, "未找到指定的列\n");
            PQclear(res);
            PQfinish(conn);
            return 1;
        }

        // 遍历结果集，获取所需两个字段的值
        for (int i = 0; i < rows; i++)
        {
            const char *value1 = PQgetvalue(res, i, column_index1);
            const char *value2 = PQgetvalue(res, i, column_index2);
            // printf("第 %d 行: Schema 的值为 %s, Name 的值为 %s\n", i + 1, value1, value2);
            configMap[value1] = value2;
        }
    }

    // 释放结果集和关闭数据库连接
    PQclear(res);
    PQfinish(conn);
    return 0;
}
// 对比两个配置文件的不同并输出
void compareConfigs(const std::unordered_map<std::string, std::string>& config1,
                    const std::unordered_map<std::string, std::string>& config2) {
    // 检查config1中有而config2中没有的配置项
    for (const auto& item : config1) {
        if (config2.find(item.first) == config2.end()) {
            std::cout << "在srouce配置文件中存在，destination配置文件中不存在: " << item.first << "=" << item.second << std::endl;
        }
    }

    // 检查config2中有而config1中没有的配置项
    for (const auto& item : config2) {
        if (config1.find(item.first) == config1.end()) {
            std::cout << "destination配置文件中存在，srouce配置文件中不存在: " << item.first << "=" << item.second << std::endl;
        }
    }

    // 检查两个配置文件中键相同但值不同的配置项
    for (const auto& item : config1) {
        auto it = config2.find(item.first);
        if (it != config2.end() && it->second != item.second) {
            std::cout << "键相同但值不同: " << item.first << " 在srouce配置文件中值为 " << item.second
                      << "，在destination配置文件中值为 " << it->second << std::endl;
        }
    }
}


int main(int argc, char **argv)
{
    const char *location;
    if (argc > 1)
        location = argv[1];
    else
        location = "config.yaml";
    std::string srouce, destination;
    srouce = getsource(location);
    destination = getdestination(location);
    // std::cout << srouce << std::endl;
    // std::cout << destination << std::endl;
    // 创建srouce数据库连接
    std::string srouceurl;
    srouceurl = getsourceurl(location);
    // std::cout << srouceurl << std::endl;
    PGconn *conn = createPGConnection(srouce);
    if (!conn)
    {
        return EXIT_FAILURE;
    }
    const char *sql = "select name,setting from pg_settings;";
    // std::cout << sql << std::endl;
    int status;
    std::unordered_map<std::string, std::string> sroucemap;
    status = QuerySettings(conn, sql, sroucemap);
    if (status != 0)
    {
        std::cerr << "连接数据库出错: " << PQerrorMessage(conn) << std::endl;
        return EXIT_FAILURE;
    }
    // 创建srouce数据库连接
    std::unordered_map<std::string, std::string> destinationmap;
    PGconn *conndestination = createPGConnection(destination);
    if (!conn)
    {
        return EXIT_FAILURE;
    }
        status = QuerySettings(conndestination, sql, destinationmap);
    if (status != 0)
    {
        std::cerr << "连接数据库出错: " << PQerrorMessage(conn) << std::endl;
        return EXIT_FAILURE;
    }
    compareConfigs(sroucemap, destinationmap);
    return 0;
}
